package cn.iocoder.yudao.framework.business.category.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.business.basic.dao.mapper.CoreMapper;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.service.CrudService;
import cn.iocoder.yudao.framework.business.basic.service.impl.BaseService;
import cn.iocoder.yudao.framework.business.category.dao.CategoryMapper;
import cn.iocoder.yudao.framework.business.category.pojo.form.AdminCategoryQueryDTO;
import cn.iocoder.yudao.framework.business.category.pojo.form.CategorySaveDTO;
import cn.iocoder.yudao.framework.business.category.pojo.po.CategoryEntity;
import cn.iocoder.yudao.framework.business.category.pojo.vo.CategorySimpleVO;
import cn.iocoder.yudao.framework.business.category.pojo.vo.CategoryVO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.KnownServiceException;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.constant.ErrorCodeConstants;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;

import java.util.*;

import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;

/**
 * 类目 Service 实现类，由子类继承，子类标注 @{@link org.springframework.stereotype.Service}
 *
 * @author 山野羡民
 */
//@org.springframework.stereotype.Service
public abstract class CategoryBaseService<DO extends CategoryEntity> extends BaseService<DO, CategoryVO, AdminCategoryQueryDTO> implements CategoryService<DO>, CrudService<DO, CategoryVO, CategorySaveDTO, AdminCategoryQueryDTO> {

    @Nonnull
    @Override
    protected CoreQueryWrapper<DO> buildQueryWrapper(AdminCategoryQueryDTO param) {
        CoreQueryWrapper<DO> wrapper = super.buildQueryWrapper(param);
        wrapper.eqIfPresent("parent_id", param.getParentId());
        wrapper.eqIfPresent("name", param.getName());
        wrapper.eqIfPresent("status", param.getStatus());
        wrapper.orderByAsc("sort");
        wrapper.orderByDesc("id");
        return wrapper;
    }

    @Override
    public DO convertToEntity(CategorySaveDTO param) {
        return BeanUtils.toBean(param, resolveEntityClass());
    }

    @Override
    public void validateCanCreate(CategorySaveDTO param, Long userId) throws KnownServiceException {
        // 校验上级编号的有效性
        validateParentCategory(null, param.getParentId());
        // 校验名称的唯一性
        validateCategoryNameUnique(param.getParentId(), param.getName(), null);
    }

    @Override
    public void validateCanUpdate(CategorySaveDTO param, Long userId) {
        // 校验存在
        validateExists(param.getId());
        // 校验上级编号的有效性
        validateParentCategory(param.getId(), param.getParentId());
        // 校验名称的唯一性
        validateCategoryNameUnique(param.getParentId(), param.getName(), param.getId());
    }

    @Override
    public void validateCanDelete(DO entity) {
        if (StrUtil.isNotBlank(entity.getCode())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_EXITS_CODE);
        }
        // 校验是否有子类目
        validateChildrenExists(entity.getId());
    }

    @Override
    public List<CategoryVO> getAllList() {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof CategoryMapper<DO> categoryBaseMapper) {
            List<DO> list = categoryBaseMapper.selectList();
            return BeanUtils.toBean(list, CategoryVO.class);
        }
        throw new KnownServiceException("Mapper 必须是" + CategoryMapper.class.getSimpleName() + " 的子类");
    }

    @Override
    public List<CategorySimpleVO> getSimpleList() {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof CategoryMapper<DO> categoryBaseMapper) {
            List<DO> list = categoryBaseMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
            return BeanUtils.toBean(list, CategorySimpleVO.class);
        }
        throw new KnownServiceException("Mapper 必须是" + CategoryMapper.class.getSimpleName() + " 的子类");
    }

    @Override
    public List<CategoryVO> getDescendantList(Long parentId, Integer status) {
        List<DO> children = new LinkedList<>();
        // 遍历每一层
        Collection<Long> parentIds = Collections.singleton(parentId);
        for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下，存在死循环
            // 查询当前层，所有的子类目
            CoreQueryWrapper<DO> wrapper = new CoreQueryWrapper<>();
            wrapper.eq("status", status);
            wrapper.inIfPresent("parent_id", parentIds);
            wrapper.orderByAsc("sort");
            wrapper.orderByDesc("id");
            List<DO> categories = getInjectedMapper().selectList(wrapper);
            // 1. 如果没有子类目，则结束遍历
            if (CollUtil.isEmpty(categories)) {
                break;
            }
            // 2. 如果有子类目，继续遍历
            children.addAll(categories);
            parentIds = convertSet(categories, CategoryEntity::getId);
        }
        return BeanUtils.toBean(children, CategoryVO.class);
    }

    @Override
    public List<CategoryVO> getEnableList(Long parentId) {
        return getDescendantList(parentId, CommonStatusEnum.ENABLE.getStatus());
    }

    @Override
    public List<CategoryVO> getList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return CollUtil.newArrayList();
        }
        CoreQueryWrapper<DO> wrapper = new CoreQueryWrapper<>();
        wrapper.in("id", ids);
        wrapper.orderByAsc("sort");
        wrapper.orderByDesc("id");
        List<DO> list = getInjectedMapper().selectList(wrapper);
        return BeanUtils.toBean(list, CategoryVO.class);
    }

    private void validateChildrenExists(Long id) {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof CategoryMapper<DO> categoryBaseMapper) {
            Long childCount = categoryBaseMapper.selectCountByParentId(id);
            if (childCount > 0) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_EXITS_CHILDREN);
            }
            return;
        }
        throw new KnownServiceException("Mapper 必须是" + CategoryMapper.class.getSimpleName() + " 的子类");
    }

    private void validateParentCategory(Long id, Long parentId) {
        if (parentId == null || CategoryEntity.PARENT_ID_ROOT.equals(parentId)) {
            return;
        }
        // 1. 不能设置自己为父类目
        if (Objects.equals(id, parentId)) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_PARENT_ERROR);
        }
        // 2. 父类目不存在
        DO parentCategory = getInjectedMapper().selectById(parentId);
        if (parentCategory == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_PARENT_NOT_EXITS);
        }
        // 3. 递归校验父类目，如果父类目是自己的子类目，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentCategory.getParentId();
            if (Objects.equals(id, parentId)) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_PARENT_IS_CHILD);
            }
            // 3.2 继续递归下一级父类目
            if (parentId == null || CategoryEntity.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            parentCategory = getInjectedMapper().selectById(parentId);
            if (parentCategory == null) {
                break;
            }
        }
    }

    private void validateCategoryNameUnique(Long parentId, String name, @Nullable Long id) {
        CoreMapper<DO> mapper = getInjectedMapper();
        if (mapper instanceof CategoryMapper<DO> categoryBaseMapper) {
            DO entity = categoryBaseMapper.selectByParentIdAndName(parentId, name);
            if (entity == null) {
                return;
            }
            // 如果 id 为空，说明不用比较是否为相同 id 的类目
            if (id == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_NAME_DUPLICATE);
            }
            if (!Objects.equals(entity.getId(), id)) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.CATEGORY_NAME_DUPLICATE);
            }
            return;
        }
        throw new KnownServiceException("Mapper 必须是" + CategoryMapper.class.getSimpleName() + " 的子类");
    }

}
